/* ************************************************************************
> File Name:     10.c
> Author:        zhfu
> mail:          3346897597@qq.com
> Created Time:  2022年05月02日 星期一 10时10分45秒
> Description:   
 ************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#define N 200 /*文件每行的最大长度，以及输入文件的路径的最大长度*/
#define M 20 /*搜索头文件的所有可能路径比如/usr/include/,/usr/local/include等*/

/*
 *用来获取需要查找的文件所在目录的路径
 *file_name是是命令行的第一个参数，可以是main.c表示当前目录下的main.c，此时cpath就是当前目录
 *也可以是./d/main.c此时的cpath就应该不是当前目录
 */
void get_cpath(char *cpath, char *file_name);

void get_name(char *name, char *line, char s);

/*
 *打印包含的所有头文件的绝对路径
 */
void print_include_files(char *file_name, char *include_search_list[]);

/*
 *获取文件名
 *不同的key对应#inclue<>和#include""，查找的目录不同得到不同的filename
 */
void get_file_name_list(int key, char *add_path, FILE *f1, char *include_search_list[]);
void free_file_name_list(char *include_search_list[]);

/*
 *改变include_search_list
 */
void modify_include(char *cpath, char *include_search_list[]);

/*
 *能否找到文件name
 */
int find_file(char *file_name);

void copy_incluede(char *dest[], char *src[]);

void get_new_file(char *new_file, char *dir, char *name);

int main(int argc, char *argv[])
{
    if (!(argc == 3 || argc == 5)){
        fputs("error, too many or too less argument!\n", stderr);
        exit(1);
    }
    else{
        if (argc == 5){
            char *tmp = argv[2];
            if (tmp[0] != '-' || tmp[1] != 'I'){
                fputs("error", stderr);
                exit(1);
            }
        }
    }

    FILE *f1;
    char *file_name = argv[1];
    char *include_search_list[M];
    int key;

    int i = 0;
    while (file_name[i]){
        ++i;
    }

    /*路径太长报错*/
    if (i + 1 >= 200){
        fputs("intput argument ", stderr);
        fputs(file_name, stderr);
        fputs("too long!\n", stderr);
    }

    FILE *f2;

    if (!(f2 = fopen(file_name, "r"))){
        fputs("open file", stderr);
        perror(file_name);
        exit(1);
    }

    if (argc == 3){
        key = 1;
        char *include_search_file = argv[2];

        if (!(f1 = fopen(include_search_file, "r"))){
            fputs("open File", stderr);
            perror(include_search_file);
            exit(1);
        }
        get_file_name_list(key, NULL, f1, include_search_list);
    }
    else{
        key = 0;
        char *include_search_file = argv[4];

        if (!(f1 = fopen(include_search_file, "r"))){
            fputs("open File", stderr);
            perror(include_search_file);
            exit(1);
        }
        char *add_path = argv[3];
        get_file_name_list(key, add_path, f1, include_search_list);
    }

    print_include_files(file_name, include_search_list);
    fclose(f1);
    fclose(f2);

    return 0;
}

void get_cpath(char *cpath, char *file_name)
{
    if (file_name[0] == '/'){
        char *tmp = strrchr(file_name, '/');
        int i = 0;
        while (file_name + i != tmp){
            cpath[i] = file_name[i];
            ++i;
        }
        cpath[i] = '\n';
    }
    else{
        getcwd(cpath, N);
        char *tmp;
        if (tmp = strchr(file_name, '/')){
            char s[N]; 
            s[0] = '/';
            int i = 0;
            while (file_name[i] != '/'){
                s[i+1] = file_name[i];
                ++i;
            }
            s[i+1] = '\n';
            s[i+2] = '\0';
            strcat(cpath, s);
        }
        else{
            char s[2] = "\n";
            strcat(cpath, s);
        }

    }
}

void get_name(char *name, char *line, char s)
{
    char *tmp;
    tmp = strchr(line, s);
    ++tmp;
    int i = 0;

    if (s == '<'){
        s = '>';
    }

    while (*tmp != s){
        name[i++] = *tmp++;
    }
    name[i] = '\0';
}

void get_file_name_list(int key, char *add_path, FILE *f1, char *include_search_list[])
{
    if (key){
        int i = 0;
        include_search_list[i] = (char *)malloc(N);
        while (fgets(include_search_list[i], N, f1)){
            ++i;
            include_search_list[i] = (char *)malloc(N);
        }
        include_search_list[i] = NULL;
    }
    else{
        int i = 0;
        include_search_list[i] = (char *)malloc(N);
        strncpy(include_search_list[i], add_path, N);
        ++i;
        include_search_list[i] = (char *)malloc(N);
        while (fgets(include_search_list[i], N, f1)){
            ++i;
            include_search_list[i] = (char *)malloc(N);
        }
        include_search_list[i] = NULL;
    }
}

void print_include_files(char *file_name, char *include_search_list[])
{
    FILE *f1;
    char line[N];
    f1 = fopen(file_name, "r");

    while (fgets(line, sizeof(line), f1)){
        if (strstr(line, "#include")){ //csevevdefvd
            int i = 0;
            while (line[i] != '<' && line[i] != '"'){
                ++i;
            }

            char name[N];
            char new_file[N];
            if (line[i] == '"'){
                char cpath[N];
                get_cpath(cpath, file_name);
                get_name(name, line, '"');
                char *include_search_list_2[M];
                copy_incluede(include_search_list_2, include_search_list);
                modify_include(cpath, include_search_list_2);

                int j = 0;
                int k = 0;
                while (include_search_list_2[j]){
                    get_new_file(new_file, include_search_list_2[j], name);
                    if (find_file(new_file)){
                        fputs(new_file, stdout);
                        fputs("\n", stdout);
                        print_include_files(new_file, include_search_list);
                        k = 1;
                        break;
                    }
                    ++j;
                }
                if (!k){
                    fputs(file_name, stderr);
                    fputs(": cannot find\n", stderr);
                }
            }
            else{
                get_name(name, line, '<');
                int j = 0;
                int k = 0;
                while (include_search_list[j]){
                    get_new_file(new_file, include_search_list[j], name);
                    if (find_file(new_file)){
                        fputs(new_file, stdout);
                        fputs("\n", stdout);
                        print_include_files(new_file, include_search_list);
                        k = 1;
                        break;
                    }
                    ++j;
                }
                if (!k){
                    fputs(file_name, stderr);
                    fputs(": cannot find\n", stderr);
                }
            }

        }
    }
}

int find_file(char *file_name)
{
    FILE *f1;

    if (!(f1 = fopen(file_name, "r"))){
        return 0;
    }

    fclose(f1);

    return 1;
}

void get_new_file(char *new_file, char *dir, char *name)
{
    int i = 0;
    int j = 0;
    while (dir[j] != '\n'){
        new_file[i] = dir[j];
        ++i;
        ++j;
    }
    new_file[i++] = '/';

    j = 0;
    while (name[j]){
        new_file[i] = name[j];
        ++i;
        ++j;
    }
    new_file[i] = '\0';
}


void modify_include(char *cpath, char *include_search_list[])
{
    int i = 0;
    while (include_search_list[i]){
        ++i;
    }

    memmove(include_search_list + 1, include_search_list, i);
    include_search_list[0] = malloc(N);
    strcpy(include_search_list[0], cpath);
}

void copy_incluede(char *dest[], char *src[])
{
    int i = 0;
    while (src[i]){
        dest[i] = (char *)malloc(N);
        strcpy(dest[i], src[i]);
        ++i;
    }
    dest[i] = NULL;
}
